$currentScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
$configFilePath = Join-Path -Path $currentScriptPath -ChildPath "deploy.json"
$winswExePath = Join-Path -Path $currentScriptPath -ChildPath "winsw.exe"
$downloadDir = Join-Path -Path $currentScriptPath -ChildPath "download"

# 默认配置
$defaultConfig = @{
    DownloadUrl = ""
    StartDllName = ""
    AppDirName = "app"
    Port = "8080"
    Environment = "Production"
    ProxyAddress = ""
    Updates = @{}
}

# 显示当前配置值并获取用户输入
function GetUserInput {
    param (
        [string]$prompt,
        [string]$currentValue
    )
    if ([string]::IsNullOrWhiteSpace($currentValue)) {
        return Read-Host "$prompt"
    } else {
        $input = Read-Host "$prompt [$currentValue]"
        if ($input -eq "/") {
            return ""
        } elseif ([string]::IsNullOrWhiteSpace($input)) {
            return $currentValue
        } else {
            return $input
        }
    }
}

# 解析 StartDllName 从 DownloadUrl
function Get-DllNameFromZip {
    param (
        [string]$zipFileName
    )
    # 去掉 .zip 后缀
    $baseName = [System.IO.Path]::GetFileNameWithoutExtension($zipFileName)
    # 分割字符串并取前面的部分
    $parts = $baseName -split '\.'
    if ($parts.Length -gt 1) {
        # 取除最后一部分外的所有部分
        $dllName = ($parts[0..($parts.Length - 2)] -join '.') + ".dll"
    } else {
        # 如果只有一个部分，则直接加上 .dll
        $dllName = $baseName + ".dll"
    }
    return $dllName
}

# 下载文件
function Download-File {
    param (
        [string]$url,
        [string]$destination
    )

    # 创建 download 目录（如果不存在）
    if (-Not (Test-Path $downloadDir)) {
        New-Item -ItemType Directory -Path $downloadDir | Out-Null
    }

    if (![string]::IsNullOrWhiteSpace($config.ProxyAddress)) {
        $parsedUrl = [uri]$url
        $newUrl = "{0}/dl/{1}" -f $config.ProxyAddress, $parsedUrl.Host + $parsedUrl.AbsolutePath
        Invoke-WebRequest -Uri $newUrl -OutFile $destination
    } else {
        Invoke-WebRequest -Uri $url -OutFile $destination
    }
}

# 确保 winsw.exe 存在
function Ensure-WinswExists {
    if (-Not (Test-Path $winswExePath)) {
        Write-Output "winsw.exe 未找到，正在下载..."

        $winswDownloadUrl = "https://soft-tools-runtime.oss-cn-chengdu.aliyuncs.com/deploy/winservice/winsw.exe"
        $destinationPath = $winswExePath

        try {
            Download-File -url $winswDownloadUrl -destination $destinationPath
            if (-Not (Test-Path $destinationPath)) {
                throw "下载winsw.exe失败"
            }
            Write-Output "winsw.exe 已下载到 $destinationPath"
        } catch {
            Write-Error "无法下载winsw.exe: $_"
            exit 1
        }
    }
}

# 定义函数

function Config-Service {
    # 获取下载URL
    $downloadUrl = GetUserInput "请输入部署包下载地址" $config.DownloadUrl

    # 解析 StartDllName 从 DownloadUrl
    $fileName = [System.IO.Path]::GetFileName($downloadUrl)
    $startDllName = Get-DllNameFromZip -zipFileName $fileName

    # 获取应用启动dll名称
    if ([string]::IsNullOrWhiteSpace($config.StartDllName)) {
        $startDllName = GetUserInput "请输入应用启动dll名称" $startDllName
    } else {
        $startDllName = GetUserInput "请输入应用启动dll名称" $config.StartDllName
    }

    # 获取应用目录名称
    $appDirName = GetUserInput "请输入应用目录名称" $config.AppDirName

    # 获取端口
    $port = GetUserInput "请输入端口" $config.Port

    # 获取环境变量
    $environment = GetUserInput "请输入环境变量" $config.Environment

    # 获取代理地址
    $proxyAddress = GetUserInput "请输入代理地址 (可选, 输入 / 表示不使用代理)" $config.ProxyAddress

    $config = @{
        DownloadUrl = $downloadUrl
        StartDllName = $startDllName
        AppDirName = $appDirName
        Port = $port
        Environment = $environment
        ProxyAddress = $proxyAddress
        Updates = $config.Updates
    }

    Save-Config

    Write-Output "配置文件已保存: $configFilePath"

    Ensure-WinswExists

    # 生成 winsw.xml 文件
    $serviceName = [System.IO.Path]::GetFileNameWithoutExtension($config.StartDllName) -replace "\.dll", ""

    $xmlContent = @"
<service>
  <id>$serviceName</id>
  <name>$serviceName Service</name>
  <description>This service runs the $serviceName application.</description>
  <executable>dotnet</executable>
  <arguments>$($config.StartDllName) --urls=http://*:$($config.Port)/ --environment $($config.Environment)</arguments>
  <logmode>rotate</logmode>
  <logpath>%BASE%\logs</logpath>
  <logsize>1M</logsize>
  <logfiles>10</logfiles>
  <workingdirectory>%BASE%\$($config.AppDirName)</workingdirectory>
</service>
"@

    Set-Content -Path (Join-Path -Path $currentScriptPath -ChildPath "winsw.xml") -Value $xmlContent -Encoding UTF8

    Write-Output "winsw.xml 已生成"
}

function Install-Service {
    if (-Not (Test-Path $configFilePath)) {
        Write-Output "请先进行配置。"
        return
    }

    $appDirectory = Join-Path -Path $currentScriptPath -ChildPath $config.AppDirName
    $zipFileName = [System.IO.Path]::GetFileName($config.DownloadUrl)
    $zipFilePath = Join-Path -Path $downloadDir -ChildPath $zipFileName

    if (Test-Path $appDirectory) {
        Write-Output "找到部署目录，直接安装..."
    } elseif (Test-Path $zipFilePath) {
        Write-Output "找到本地部署包，正在解压..."
        if (Test-Path $zipFilePath) {
            Expand-Archive -Path $zipFilePath -DestinationPath $appDirectory -Force
            Write-Output "部署包已成功解压到 $appDirectory"
        } else {
            Write-Error "本地部署包路径无效: $zipFilePath"
            return
        }
    } else {
        Write-Output "本地部署包不存在，正在下载部署包..."
        Download-File -url $config.DownloadUrl -destination $zipFilePath
        if (Test-Path $zipFilePath) {
            Expand-Archive -Path $zipFilePath -DestinationPath $appDirectory -Force
            Write-Output "部署包已成功解压到 $appDirectory"
        } else {
            Write-Error "下载的部署包路径无效: $zipFilePath"
            return
        }
    }

    Ensure-WinswExists

    & $winswExePath install

    Write-Output "服务已安装"
}

function Start-Service {
    if (-Not (Test-Path $configFilePath)) {
        Write-Output "请先进行配置。"
        return
    }

    Ensure-WinswExists

    Write-Output "启动服务..."

    & $winswExePath start

    Write-Output "服务已启动。"
}

function Stop-Service {
    if (-Not (Test-Path $configFilePath)) {
        Write-Output "请先进行配置。"
        return
    }

    Ensure-WinswExists

    Write-Output "停止服务..."

    & $winswExePath stop

    Write-Output "服务已停止。"
}

function Uninstall-Service {
    if (-Not (Test-Path $configFilePath)) {
        Write-Output "请先进行配置。"
        return
    }

    Ensure-WinswExists

    Write-Output "卸载服务..."

    if ((& $winswExePath status) -match 'RUNNING') {
        & $winswExePath stop
    }
    & $winswExePath uninstall

    Write-Output "服务已卸载。"
}

function Update-Service {
    if (-Not (Test-Path $configFilePath)) {
        Write-Output "请先进行配置。"
        return
    }

    $zipFileName = [System.IO.Path]::GetFileName($config.DownloadUrl)
    $zipFilePath = Join-Path -Path $downloadDir -ChildPath $zipFileName
    $appDirName = $config.AppDirName

    # 备份当前配置和应用目录
    $backupTime = Get-Date -Format "yyyyMMddHHmmss"
    $backupDir = Join-Path $currentScriptPath "backup_$backupTime"
    New-Item -ItemType Directory -Path $backupDir | Out-Null
    
    # 备份配置文件
    Copy-Item -Path $configFilePath -Destination $backupDir
    Copy-Item -Path (Join-Path $currentScriptPath "winsw.xml") -Destination $backupDir
    
    # 备份应用目录
    $appBackupDir = Join-Path $backupDir $config.AppDirName
    if (Test-Path $config.AppDirName) {
        Copy-Item -Recurse -Path $config.AppDirName -Destination $appBackupDir
    }
    
    Write-Output "已备份配置文件和应用程序到 $backupDir"

    # 下载并校验部署包
    Write-Output "正在下载最新的部署包..."
    try {
        Download-File -url $config.DownloadUrl -destination $zipFilePath
        if (-Not (Test-Path $zipFilePath)) {
            throw "下载部署包失败"
        }
        
        # 计算校验和
        $zipHash = (Get-FileHash -Algorithm SHA256 -Path $zipFilePath).Hash
        Write-Output "部署包校验和: $zipHash"
    } catch {
        Write-Error "下载部署包失败: $_"
        exit 1
    }

    Write-Output "正在停止服务..."
    if ((& $winswExePath status) -match 'RUNNING') {
        & $winswExePath stop
    }

    #Write-Output "正在清空目标目录..."
    #Remove-Item -Recurse -Force -Path $appDirName\*

    Write-Output "正在解压覆盖应用目录..."
    try {
        Expand-Archive -Path $zipFilePath -DestinationPath $appDirName -Force
        if (-Not (Test-Path (Join-Path $appDirName $config.StartDllName))) {
            throw "解压部署包失败，未找到启动文件"
        }
        Write-Output "部署包已成功解压到 $appDirName"
    } catch {
        Write-Error "解压部署包失败: $_"
        
        # 回滚操作
        Write-Output "正在回滚到备份版本..."
        Copy-Item -Path (Join-Path $backupDir "deploy.json") -Destination $currentScriptPath -Force
        Copy-Item -Path (Join-Path $backupDir "winsw.xml") -Destination $currentScriptPath -Force
        Write-Output "已恢复配置文件"
        
        exit 1
    }

    Write-Output "正在启动服务..."
    & $winswExePath start
    if ($LASTEXITCODE -ne 0) {
        Write-Error "启动服务失败"
        exit 1
    }

    Write-Output "服务已更新。"
    Write-Output "部署包校验和: $zipHash"
}

function Download-Custom-Package {
    $customUrl = Read-Host "请输入要下载的文件URL"
    if ([string]::IsNullOrWhiteSpace($customUrl)) {
        Write-Error "URL不能为空，请重新尝试。"
        return
    }

    $fileName = [System.IO.Path]::GetFileName($customUrl)
    $destinationPath = Join-Path -Path $downloadDir -ChildPath $fileName

    Write-Output "正在下载文件到 $destinationPath..."
    Download-File -url $customUrl -destination $destinationPath

    Write-Output "文件已成功下载到 $destinationPath"
}

# 新增函数：配置自定义更新
function Config-Custom-Update {
    $scriptName = GetUserInput "请输入自定义更新的名称 (例如 admin)"

    if ($config.Updates.PSObject.Properties.Name -contains $scriptName) {
        $currentDownloadUrl = $config.Updates.$scriptName.DownloadUrl
        $currentTargetDir = $config.Updates.$scriptName.TargetDir
    } else {
        $currentDownloadUrl = ""
        $currentTargetDir = ""
    }

    $downloadUrl = GetUserInput "请输入下载更新包的地址" $currentDownloadUrl
    $targetDir = GetUserInput "请输入目标目录" $currentTargetDir

    $config.Updates | Add-Member -MemberType NoteProperty -Name $scriptName -Value @{
        DownloadUrl = $downloadUrl
        TargetDir = $targetDir
    } -Force

    Save-Config

    Write-Output "自定义更新配置已保存: $configFilePath"
}

# 新增函数：执行自定义更新
function Rollback-Service {
    # 查找最新的备份目录
    $backupDirs = Get-ChildItem -Path $currentScriptPath -Directory | Where-Object { $_.Name -match "^backup_" } | Sort-Object LastWriteTime -Descending
    
    if ($backupDirs.Count -eq 0) {
        Write-Error "没有可用的备份"
        return
    }

    $latestBackup = $backupDirs[0].FullName

    Write-Output "正在从备份恢复: $latestBackup"
    
    # 停止服务
    if ((& $winswExePath status) -match 'RUNNING') {
        & $winswExePath stop
    }

    try {
        # 恢复配置文件
        Copy-Item -Path (Join-Path $latestBackup "deploy.json") -Destination $currentScriptPath -Force
        Copy-Item -Path (Join-Path $latestBackup "winsw.xml") -Destination $currentScriptPath -Force
        
        # 恢复应用目录
        $appBackupDir = Join-Path $latestBackup $config.AppDirName
        if (Test-Path $appBackupDir) {
            Remove-Item -Recurse -Force -Path $config.AppDirName -ErrorAction SilentlyContinue
            Copy-Item -Recurse -Path $appBackupDir -Destination $config.AppDirName
        }
        
        Write-Output "已成功恢复备份"
    } catch {
        Write-Error "恢复备份失败: $_"
        exit 1
    }

    # 启动服务
    Write-Output "正在启动服务..."
    & $winswExePath start
    if ($LASTEXITCODE -ne 0) {
        Write-Error "启动服务失败"
        exit 1
    }

    Write-Output "服务已从备份恢复并启动"
}

function Update-Custom-Update {
    $scriptName = GetUserInput "请输入要执行的自定义更新名称"

    if (-Not ($config.Updates.PSObject.Properties.Name -contains $scriptName)) {
        Write-Error "未找到名为 $scriptName 的自定义更新配置。"
        return
    }

    $downloadUrl = $config.Updates.$scriptName.DownloadUrl
    $targetDir = $config.Updates.$scriptName.TargetDir

    $zipFileName = [System.IO.Path]::GetFileName($downloadUrl)
    $zipFilePath = Join-Path -Path $downloadDir -ChildPath $zipFileName

    Write-Output "正在下载最新的部署包..."
    Download-File -url $downloadUrl -destination $zipFilePath

    # 检查目标目录是否存在
    if (Test-Path $targetDir) {
        Write-Output "正在清空目标目录..."
        Remove-Item -Recurse -Force -Path $targetDir\*
    } else {
        Write-Output "目标目录 $targetDir 不存在，跳过清空操作。"
    }

    Write-Output "正在解压到目标目录..."
    if (Test-Path $zipFilePath) {
        Expand-Archive -Path $zipFilePath -DestinationPath $targetDir -Force
        Write-Output "部署包已成功解压到 $targetDir"
    } else {
        Write-Error "下载的部署包路径无效: $zipFilePath"
        return
    }

    Write-Output "自定义更新完成。"
}

# 保存配置到 deploy.json
function Save-Config {
    # 确保 Updates 是一个 PSCustomObject
    if (-Not ($config.Updates -is [PSCustomObject])) {
        $config.Updates = [PSCustomObject]@{}
    }

    if ($PSVersionTable.PSVersion.Major -ge 6) {
        ConvertTo-Json -InputObject $config -Depth 10 | Out-File -FilePath $configFilePath -Encoding utf8BOM
    } else {
        $jsonContent = ConvertTo-Json -InputObject $config -Depth 10
        $bom = [byte[]]@(0xEF, 0xBB, 0xBF)
        $bytes = [System.Text.Encoding]::UTF8.GetBytes($jsonContent)
        [System.IO.File]::WriteAllBytes($configFilePath, $bom + $bytes)
    }
}

# 循环执行操作
while ($true) {
    do {
        $Action = Read-Host "请输入操作 (config/install/start/stop/uninstall/update/download/config-u/update-c/rollback/exit)"
    } while ("config", "install", "start", "stop", "uninstall", "update", "download", "config-u", "update-c", "rollback", "exit" -notcontains $Action)

    # 加载配置文件
    if (Test-Path $configFilePath) {
        try {
            $config = Get-Content -Path $configFilePath | ConvertFrom-Json
            # 确保 Updates 是一个 PSCustomObject
            if (-Not ($config.Updates -is [PSCustomObject])) {
                $config.Updates = [PSCustomObject]@{}
            }
        } catch {
            Write-Error "配置文件格式错误: $_"
            exit 1
        }
    } else {
        $config = $defaultConfig
    }

    switch ($Action) {
        "config" {
            Config-Service
            break
        }
        "install" {
            Install-Service
            break
        }
        "start" {
            Start-Service
            break
        }
        "stop" {
            Stop-Service
            break
        }
        "uninstall" {
            Uninstall-Service
            break
        }
        "update" {
            Update-Service
            break
        }
        "download" {
            Download-Custom-Package
            break
        }
        "config-u" {
            Config-Custom-Update
            break
        }
        "update-c" {
            Update-Custom-Update
            break
        }
        "rollback" {
            Rollback-Service
            break
        }
        "exit" {
            Write-Output "退出脚本。"
            exit 0
        }
    }

    # 重置 Action 以便下次循环
    $Action = $null
}
